﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace IEx
{
    public class DefaultRequestBuilder
    {
        protected NameValueCollection paramCollection;
        private SourceItem souceItem;

        public DefaultRequestBuilder()
            : this("GET", "text/html")
        {
        }

        public DefaultRequestBuilder(string httpMethod, string contentType)
        {
            this.Method = httpMethod;
            this.ContentType = contentType;
            this.paramCollection = new NameValueCollection();
        }

        public virtual string Method
        {
            get;
            protected set;
        }

        public virtual string ContentType
        {
            get;
            protected set;
        }

        public SourceItem SourceItem
        {
            get;
            internal set;
        }

        /// <summary>
        /// Clone current object with values
        /// </summary>
        /// <returns></returns>
        public virtual DefaultRequestBuilder Clone()
        {
            DefaultRequestBuilder cloneObject = new DefaultRequestBuilder(this.Method, this.ContentType);
            int count = this.paramCollection.Count;
            foreach (string key in this.paramCollection.AllKeys)
            {
                cloneObject.AttachRequestParam(key, this.paramCollection[key]);
            }

            return cloneObject;
        }

        public virtual void BeginBuildRequest(SourceItem item)
        {
        }

        /// <summary>
        /// Build a request and write parameters into request body to send to target server
        /// </summary>
        /// <param name="request">Request to build</param>
        public virtual void BuildRequest(ref HttpWebRequest request)
        {
            if (request == null)
                throw new NullReferenceException("@request cannot be Null");

            request.Method = this.Method;
            request.ContentType = this.ContentType;
            byte[] content = this.GetRequestContent();
            int contentLength = content.Length;
            if (contentLength > 0)
            {
                request.ContentLength = contentLength;

                Stream requestStream = request.GetRequestStream();
                requestStream.Write(content, 0, contentLength);
                requestStream.Close();
            }
        }

        /// <summary>
        /// Get content of request body
        /// </summary>
        /// <returns></returns>
        public byte[] GetRequestContent()
        {
            StringBuilder requestPrams = new StringBuilder();
            List<string> paramList = new List<string>();
            foreach (string key in this.paramCollection.AllKeys)
            {
                paramList.Add(string.Format(
                    "{0}={1}",
                    Uri.EscapeDataString(key),
                    Uri.EscapeDataString(this.paramCollection[key])));
            }
            return Encoding.UTF8.GetBytes(String.Join("&", paramList));
        }

        /// <summary>
        /// Add a request parameter to current request object.
        /// If parameter with the passed <see cref="name"/> is already exist, the new value is concatenated with old values and separated by comma sign
        /// </summary>
        /// <param name="name">Name of parameter</param>
        /// <param name="value">Value of parameter</param>
        public void AttachRequestParam(string name, string value)
        {
            if (!string.IsNullOrEmpty(name))
                this.paramCollection.Add(name, value);
        }

        /// <summary>
        /// Remove a request parameter from current request object
        /// </summary>
        /// <param name="name"></param>
        public void DetachRequestParam(string name)
        {
            if (this.paramCollection.AllKeys.Any(k => k == name))
            {
                this.paramCollection.Remove(name);
            }
        }

        /// <summary>
        /// Change the current value with name but not concatnate values
        /// </summary>
        /// <param name="name"></param>
        /// <param name="value"></param>
        public void SetRequestParam(string name, string value)
        {
            if (this.paramCollection.AllKeys.Any(k => k == name))
            {
                this.paramCollection.Set(name, value);
            }
            else
            {
                this.AttachRequestParam(name, value);
            }
        }
    }
}
